//
//  tun.c
//  myvpn
//
//  Created by Zhuobin He on 12-12-31.
//  Copyright (c) 2012骞�Zhuobin He. All rights reserved.
//

#include "SysLib.h"

#ifndef OS_WIN

#include "TunDev.h"

void tundev_delete(PTunDev obj) {
    if (obj->dev) free(obj->dev);
    if (obj->local_ip) free(obj->local_ip);
    if (obj->remote_ip) free(obj->remote_ip);
    free(obj);
}

PTunDev tundev_alloc(const char *dev) {
    int fd;
#ifdef OS_LINUX
    char clonedev[] = "/dev/net/tun";
    if (-1 == access(clonedev, 0)) {
        strcpy(clonedev, "/dev/tun");
    }
#else
    char clonedev[16] = "/dev/";
    strcpy(clonedev + 5, dev);
    if (access(clonedev, 0) == -1) {
        if (0 != system("kextload /Applications/Tunnelblick.app/Contents/Resources/tun.kext/")) {
            return NULL;
        }
    }
#endif
    if( (fd = open(clonedev , O_RDWR)) < 0 ) {
        log_error("Error opening %s", clonedev);
        return NULL;
    }
    
#ifdef OS_LINUX
    struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));
    
    ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
    
    if (*dev) {
        strcpy(ifr.ifr_name, dev);
    }
    int err;
    if( (err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0 ) {
        log_error("Error ioctl(TUNSETIFF)");
        close(fd);
        return NULL;
    }
    
//    strcpy(dev, ifr.ifr_name);
#endif
    PTunDev ret = NULL;
    zmalloc(&ret, sizeof(TunDev));
    ret->dev = string_alloc(dev);
    ret->fd = fd;
    return ret;
}

int tundev_ifconfig(PTunDev dev, const char *local_ip, const char *remote_ip) {
    char cmd[256];
    dev->local_ip = string_alloc(local_ip);
    dev->remote_ip = string_alloc(remote_ip);
#ifdef OS_LINUX
    sprintf(cmd, "/sbin/ifconfig %s %s pointopoint %s up", dev->dev, local_ip, remote_ip);
    log_info("sh: %s", cmd);
    if (0 == system(cmd)) return 0;
    sprintf(cmd, "/sbin/ifconfig %s %s dstaddr %s up", dev->dev, local_ip, remote_ip);
    log_info("sh: %s", cmd);
    if (0 == system(cmd)) return 0;
#else
    sprintf(cmd, "/sbin/ifconfig %s %s %s up", dev->dev, local_ip, remote_ip);
    log_info("sh: %s", cmd);
    if (0 == system(cmd)) return 0;
#endif
    return -1;
}


void tundev_close(PTunDev dev) {
    log_info("Closing tun dev: %s", dev->dev);
    close(dev->fd);
}

int tundev_read(PTunDev dev, char *buffer, int limit) {
    return (int)read(dev->fd, buffer, limit);
}

int tundev_write(PTunDev dev, const char *data, int size) {
    return (int)write(dev->fd, data, size);
}

int tundev_route(PTunDev dev, const char *address, int prefix) {
    char cmdstr[256];
    unsigned int mask_int = 0;
    char mask_str[20] = "";
    int i;
    for (i=0; i<prefix; i++) {
        mask_int |= 1u << (31 - i);
    }
    sprintf(mask_str, "%d.%d.%d.%d",
            (mask_int >> 24) & 0xff, (mask_int >> 16) & 0xff,
            (mask_int >> 8) & 0xff, mask_int & 0xff);

#ifdef OS_LINUX
    sprintf(cmdstr, "route add -net %s netmask %s dev %s > /dev/null", address, mask_str, dev->dev);
#else
    sprintf(cmdstr, "route add -net %s -netmask %s %s > /dev/null", address, mask_str, dev->remote_ip);
#endif
//    log_trace("sh: %s", cmdstr);
    int ret = system(cmdstr);
//    if (ret != 0) {
//#ifdef OS_LINUX
//        sprintf(cmdstr, "route change -net %s netmask %s dev %s", address, mask_str, dev->dev);
//#else
//        sprintf(cmdstr, "route change -net %s -netmask %s %s", address, mask_str, dev->remote_ip);
//#endif
//        log_debug("Add fail, retry with change");
//        ret = system(cmdstr);
//    }
    return ret;
}

#endif

